#include "./../inc/view.h"
#include "./../inc/handle.h"

#include "stack.h"
#include "template.h"
#include "animation.h"

#include <string.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>

#include "xml.h"

#include "cairo/cairo.h"

#include "render.h"
#include "system.h"
#include "script.h"
#include "log.h"
#include "./../inc/button.h"

typedef struct _UFO_View_Internal
{
	ufoRect rect;
	VIEWPROC proc; 
	struct _UFO_View_Internal *parentView, *childView, *nextView, *lastView; 
	UFO_View handle; 
	UFO_LAYOUT_ANCHOR anchor; 
	ufoRect margin; 
	float zOrder;
	float alpha;
	ufoColor *backgroundColor;

	//### only for 3D
	float angle;
	float hotSpot_x, hotSpot_y;
	UFO_AXIS axis;//### z = 1, x = 2, y = 4
	float z;
	float fovy;
	int perspective;
	//### for 3D

	unsigned int mask;
	char *id; 
	void *userData; 

	char *onInit;
	UFO_Texture background; 

	char *onTouchDown;
	char *onTouchUp;
	char *onTouchClick;
	char *onAnimationFinish;
	char *onGesturePan;
	char *onGestureSwipe;
	char *onRectChange;

	//### for corner
	float cornerRadius;
	HCANVAS renderTarget;
	HTEXTURE cornerTex;
	//### for corner

	UFO_Stack touchTrack;

    UFO_GESTURE gesture;
}UFO_View_Internal, *PUFO_View_Internal;

typedef struct _UFO_Touch_Track
{
	long timeStamp;
	ufoPoint point;
}UFO_Touch_Track;

UFO_View g_rootView;
UFO_View g_captureView;
UFO_View g_hoverView;

//### for pan gesture
int g_internal_is_pan;
ufoPoint g_internal_touchDown_point[10];
//###

//### for pinch gesture
int g_internal_is_pinch;
//###

#define GESTURE_SWIPE_TIME 90

int rootView_Proc(UFO_View view, int id, void* wParam, void* lParam);
int internal_rootView_init();
UFO_View internal_ufoViewCreate(const char* className);
int internal_ufoViewAddChildView(UFO_View parentView, UFO_View childView);

int internal_initializationScript_view();
int internal_initializationScript_animation();
int internal_initializationScript_flipview();
int internal_initializationScript_static();
int internal_initializationScript_buttonview();
int internal_initializationScript_scrollview();
int internal_initializationScript_pagecontrol();
int internal_initializationScript_tabview();
int internal_initializationScript_tabbar();

int default_thumb_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_scrollBar_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_scrollView_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_content_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_static_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_button_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_flipView_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_waterView_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_pagecontrol_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_tabview_proc(UFO_View view, int id, void* wParam, void* lParam);
int default_tabbar_proc(UFO_View view, int id, void* wParam, void* lParam);


HTEXTURE internal_general_cornerTexture(float width, float height, float radius, float dpi)
{
	HTEXTURE texture = 0;
	if (dpi == 0) {
		dpi = ufoGetDPI();
	}

	width *= dpi;
	height *= dpi;
	radius *= dpi; 

	{
		cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int)width, (int)height); 
		cairo_t * cr = cairo_create(surface);

		unsigned char *data = 0;

		double x         = 0,        /* parameters like cairo_rectangle */
			y         = 0;
		//corner_radius = height / 10.0;   /* and corner curvature radius */

		double degrees = 3.1415926 / 180.0;

		cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);

		cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
		cairo_rectangle (cr, 0, 0, width, height);
		cairo_fill(cr);

		cairo_new_sub_path (cr);
		cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
		cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
		cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
		cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
		cairo_close_path (cr);

		cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
		cairo_fill_preserve (cr);
		cairo_set_line_width (cr, 0.0);
		cairo_stroke (cr);

		data = cairo_image_surface_get_data(surface);

		texture = ufoGenTextures(data, (int)width, (int)height, 4);

		cairo_destroy(cr);
		cairo_surface_destroy(surface);
	}
	return texture;
}

int recursive_Paint(UFO_View view, int id, void* wParam, void* lParam)
{
	PUFO_View_Internal pView = (PUFO_View_Internal)ufoObjectFromHandle((UFO_Handle)view);
	PUFO_View_Internal nextView = 0;

	if (pView) {

		nextView = pView->childView;
		while (nextView) {
			UFO_PAINT_STRUCT paint = *((UFO_PAINT_STRUCT*)lParam);
            float alpha = nextView->alpha;
			ufoRect *fresh = (ufoRect*)wParam;
			ufoRect rect = nextView->rect;
			ufoRect clipIntersection = {0};
			int bTransform3d = 0;
			HCANVAS curCanvas = ufoCanvasGetCurrent();

			if (ufoViewGetMask(nextView->handle, UFO_VIEW_BIT_HIDDEN) || alpha == 0.0f || paint.alpha == 0.0f) {//### hidden or alpha be zero
				nextView = nextView->nextView;
				continue;
			}

			ufoViewRectToScreen(pView->handle, &rect);
			ufoRectIntersection(fresh, &rect, &clipIntersection);

			if (!ufoRectIsEmpty(&clipIntersection)) {

				ufoRenderPushMatrix(UFO_MODELVIEW);
				ufoRenderTranslate(UFO_MODELVIEW, pView->rect.left, pView->rect.top, 0);
				
				if (nextView->angle) {
					ufoRenderTranslate(UFO_MODELVIEW, (nextView->rect.right-nextView->rect.left)/2, (nextView->rect.bottom-nextView->rect.top)/2, 0);
					ufoRenderTranslate(UFO_MODELVIEW, nextView->hotSpot_x, nextView->hotSpot_y, -nextView->z);
					if (!nextView->axis) 
						ufoRenderRotate(UFO_MODELVIEW, nextView->angle, 0, 0, 1);
					else
						ufoRenderRotate(UFO_MODELVIEW, nextView->angle, (float)!!(nextView->axis&1), 
						(float)!!(nextView->axis&2), (float)!!(nextView->axis&4));
					ufoRenderTranslate(UFO_MODELVIEW, -nextView->hotSpot_x, -nextView->hotSpot_y, 0);
					ufoRenderTranslate(UFO_MODELVIEW, -(nextView->rect.right-nextView->rect.left)/2, -(nextView->rect.bottom-nextView->rect.top)/2, 0);
				}
				
				if (nextView->mask & UFO_VIEW_BIT_CLIPSTOBOUNDS) {
					if (paint.stencilTest == 0) {
                        ufoSetStencilTest(1);
                    }
                    
					if (ufoGetStencilTest()) {
						UFO_Texture background = nextView->background;

						ufoSetStencilFunc(RENDER_CMP_ALWAYS);
						ufoSetStencilTestRef(paint.stencilTest);
						ufoSetStencilOp(UFO_STENCIL_KEEP, UFO_STENCIL_KEEP, UFO_STENCIL_INCR);

						nextView->background = 0;
						ufoViewDefaultProc(nextView->handle, MSG_PAINT, wParam, &paint);
						nextView->background = background;

						paint.stencilTest++;
						ufoSetStencilFunc(RENDER_CMP_EQUAL);
						ufoSetStencilTestRef(paint.stencilTest);
						ufoSetStencilOp(UFO_STENCIL_KEEP, UFO_STENCIL_KEEP, UFO_STENCIL_KEEP);	
					}
				}

				if (nextView->cornerRadius != 0.0f && (nextView->mask & UFO_VIEW_BIT_CLIPSTOBOUNDS)) {
					ufoRect rcCanvas = nextView->rect;
                    ufoRectMoveTo(&rcCanvas, 0, 0);

					if (!nextView->renderTarget) {
						nextView->renderTarget = ufoCreateCanvas((rcCanvas.right-rcCanvas.left), (rcCanvas.bottom-rcCanvas.top));
					}
                    
                    if (!nextView->cornerTex) {
                        nextView->cornerTex = internal_general_cornerTexture((rcCanvas.right-rcCanvas.left), (rcCanvas.bottom-rcCanvas.top), nextView->cornerRadius, 0);
                    }

					ufoSetCanvas(nextView->renderTarget);
                    
					ufoClearColor(0.0f, 0.0f, 0.0f, 0.0f);
					ufoClearDepth(0.0f);
					ufoClearStencil(paint.stencilTest);
                    
                    ufoPushViewPort();
					ufoSetViewPort(&rcCanvas);
                    
					ufoRenderPushMatrix(UFO_PROJECTION);
                    ufoRenderPushMatrix(UFO_MODELVIEW);
                    
                    ufoPresent2D((rcCanvas.right-rcCanvas.left), (rcCanvas.bottom-rcCanvas.top));
                    ufoRenderIdentityMatrix(UFO_MODELVIEW);
                    ufoRenderTranslate(UFO_MODELVIEW, -nextView->rect.left, -nextView->rect.top, 0);
				}
				else if (nextView->cornerRadius != 0.0f) {
					ufoRect rcCanvas = nextView->rect;
					if (!nextView->cornerTex) {
						nextView->cornerTex = internal_general_cornerTexture((rcCanvas.right-rcCanvas.left), (rcCanvas.bottom-rcCanvas.top), nextView->cornerRadius, 0);
					}
					paint.mask = nextView->cornerTex;
				}

				paint.alpha *= alpha;
				paint.color = nextView->backgroundColor;
                nextView->proc(nextView->handle, id, wParam, &paint);//### Notify Paint Message
				paint.mask = 0;
				paint.color = 0;

				if (nextView->perspective) {//### 3d view
					bTransform3d = 1;

					ufoRenderPushMatrix(UFO_PROJECTION);
					ufoRenderPushMatrix(UFO_MODELVIEW);

					ufoPresent3D(rect.right-rect.left, rect.bottom-rect.top, nextView->fovy);
					ufoRenderTranslate(UFO_MODELVIEW, -nextView->rect.left, -nextView->rect.top, 0);				
					
					if (!nextView->renderTarget) {
						ufoPushViewPort();
						ufoSetViewPort(&rect);
					}
				}
                
				recursive_Paint((UFO_View)nextView->handle, id, wParam, &paint);
				
				if (bTransform3d) {
					ufoRenderPopMatrix(UFO_PROJECTION);
					ufoRenderPopMatrix(UFO_MODELVIEW);

					if (!nextView->renderTarget) {
						ufoPopViewPort();
					}
				}

				if (nextView->cornerRadius != 0.0f && (nextView->mask & UFO_VIEW_BIT_CLIPSTOBOUNDS)) {
					ufoRect rcCanvas = nextView->rect;
					
					ufoSetCanvas(curCanvas);
                    ufoPopViewPort();
                    ufoRenderPopMatrix(UFO_PROJECTION);
                    ufoRenderPopMatrix(UFO_MODELVIEW);
                    
                    ufoBlendCanvas(nextView->renderTarget, nextView->cornerTex, &rcCanvas, paint.color, paint.alpha, 0.0f);
				}

				if (nextView->mask & UFO_VIEW_BIT_CLIPSTOBOUNDS) {
					paint.stencilTest--;
                    if (ufoGetStencilTest()) {
						UFO_Texture background = nextView->background;

						ufoSetStencilFunc(RENDER_CMP_ALWAYS);
						ufoSetStencilTestRef(paint.stencilTest);
						ufoSetStencilOp(UFO_STENCIL_KEEP, UFO_STENCIL_KEEP, UFO_STENCIL_DESR);

						nextView->background = 0;
						ufoViewDefaultProc(nextView->handle, MSG_PAINT, wParam, &paint);
						nextView->background = background;

						ufoSetStencilFunc(RENDER_CMP_ALWAYS);
						ufoSetStencilOp(UFO_STENCIL_KEEP, UFO_STENCIL_KEEP, UFO_STENCIL_KEEP);
					}
                    
                    if (paint.stencilTest == 0) {
                        ufoSetStencilTest(0);
                    }
				}

				ufoRenderPopMatrix(UFO_MODELVIEW);
			}// chip with dirty area
			nextView = nextView->nextView;
		}// while (nextView)

	}// if (view)
	return 1;
}

int recursive_SizeChange(UFO_View view, int id, void* wParam, void* lParam)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	PUFO_View_Internal pParentView = pView->parentView;
	ufoRect *newRect = (ufoRect*)wParam;

	if (pView) {
		if ((pView->anchor || pView->mask & UFO_VIEW_BIT_AUTOSIZE) && pParentView) {//### if has layout, it may modify newRect
			const ufoRect *parentRect = &pParentView->rect;
			const ufoRect *margin = &pView->margin;
			UFO_LAYOUT_ANCHOR anchor = pView->anchor;

			float newWidth = newRect->right - newRect->left;
			float newHeight = newRect->bottom - newRect->top;

			if (pView->mask & UFO_VIEW_BIT_AUTOSIZE) {
				ufoSize sizeCalc = {0};
				ufoViewSendMessage(view, MSG_CALC_SIZE, &sizeCalc, 0);
				newWidth = sizeCalc.width;
				newHeight = sizeCalc.height;
				newRect->right = newRect->left+newWidth;
				newRect->bottom = newRect->top+newHeight;
			}

			if (anchor & ANCHOR_CENTER_HORZ) {
				newRect->left = (parentRect->right-parentRect->left-newWidth)/2;
				newRect->right = newRect->left+newWidth;

				newRect->left += margin->left;
				newRect->right += margin->left;

				newRect->left -= margin->right;
				newRect->right -= margin->right;
			}

			if (anchor & ANCHOR_CENTER_VERT) {
				newRect->top = (parentRect->bottom-parentRect->top-newHeight)/2;
				newRect->bottom = newRect->top+newHeight;

				newRect->top += margin->top;
				newRect->bottom += margin->top;

				newRect->top -= margin->bottom;
				newRect->bottom -= margin->bottom;
			}

			if (anchor & ANCHOR_LEFT) {
				newRect->left = margin->left;

				if ((anchor & ANCHOR_RIGHT) == 0) {
					newRect->right = newRect->left+newWidth;
				}
			}

			if (anchor & ANCHOR_TOP) {
				newRect->top = margin->top;

				if ((anchor & ANCHOR_BOTTOM) == 0) {
					newRect->bottom = newRect->top+newHeight;
				}
			}

			if (anchor & ANCHOR_RIGHT) {
				newRect->right = parentRect->right - parentRect->left-margin->right;

				if ((anchor & ANCHOR_LEFT) == 0) {
					newRect->left = newRect->right - newWidth;
				}
			}

			if (anchor & ANCHOR_BOTTOM) {
				newRect->bottom = parentRect->bottom - parentRect->top-margin->bottom;

				if ((anchor & ANCHOR_TOP) == 0) {
					newRect->top = newRect->bottom - newHeight;
				}
			}
		}//### end layout

		if (!ufoRectEqual(newRect, &pView->rect)) {//### rect ready changed

			PUFO_View_Internal pChildView = pView->childView;  
			pView->rect = *newRect;
			ufoViewSetNeedPaint((UFO_View)pView->handle);

			if (view != g_rootView) {
				pView->proc(view, id, newRect, 0);
			}

			ufoViewSetNeedPaint((UFO_View)pView->handle);//### rect changed, Need Paint        

			while (pChildView) {
				ufoRect childRect = pChildView->rect;
				recursive_SizeChange((UFO_View)pChildView->handle, id, &childRect, 0);
				pChildView = pChildView->nextView;
			}
		}
	}
	return 1;
}

int internal_route_paint(void* wParam, void* lParam)
{
	return rootView_Proc(g_rootView, MSG_PAINT, wParam, lParam);
}

int internal_sort_children_views(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		int nCount = ufoViewGetChildCount(view);
		if (nCount) {
			int nSize = nCount;
			int bSordNeed = 0;
			PUFO_View_Internal *views = (PUFO_View_Internal*)malloc(sizeof(PUFO_View_Internal)*nCount);
			PUFO_View_Internal pSubView = pView->childView;
			while (nSize-- && pSubView) {
				views[nSize] = pSubView;
				if (pSubView->zOrder != 0) {
					bSordNeed = 1;
				}
				pSubView = pSubView->nextView;
			}

			if (bSordNeed) {//### reOrder zOrder
				int i, j;
                
                for (i = 0; i < nCount; ++i) {
                    for (j = nCount-1; j > i; --j) {
                        if (views[j]->zOrder > views[i]->zOrder) {
                            PUFO_View_Internal temp = views[i];
                            views[i] = views[j];
                            views[j] = temp;
                        }
                    }
                }
                                
                pView->childView = views[0];
				views[nCount-1]->nextView = 0;
				views[0]->lastView = 0;
				for (i = 0; i < nCount-1; ++i) {
					views[i]->nextView = views[i+1];
				}
				for (i = 1; i < nCount; ++i) {
					views[i]->lastView = views[i-1];
				}				
			}

			free(views);
		}
		return 1;
	}
	return 0;
}

int internal_route_sizeChanged(void* wParam, void* lParam)
{
	ufoRect *rectSize = (ufoRect*)wParam;//### size rect is pixel, must be div dpi
	rectSize->right /= ufoGetDPI();
	rectSize->bottom /= ufoGetDPI();

	ufoRestore(wParam, lParam);//### size changed, restore must

	if (!g_rootView) {
		internal_rootView_init();
		ufoInitialization();
	}

	ufoAddFreshRect(0);//### fullscreen need reDraw

	return rootView_Proc(g_rootView, MSG_SIZE_CHANGE, wParam, lParam);
}

int internal_route_touchEvent(int msg, void* wParam, void* lParam)
{
    int count = *(int*)lParam;
	ufoPoint* pt = (ufoPoint*)wParam;
	for (int i = 0; i < count; ++i) {
        pt->x /= ufoGetDPI();
        pt->y /= ufoGetDPI();
        ++pt;
    }
	return rootView_Proc(g_rootView, msg, wParam, lParam);
}

int internal_route_ncHitTest(void* wParam, void* lParam)
{
	ufoPoint* pt = (ufoPoint*)wParam;
	pt->x /= ufoGetDPI();
	pt->y /= ufoGetDPI();
	return rootView_Proc(g_rootView, MSG_NC_HITTEST, wParam, lParam);
}

UFO_View internal_get_commonView(UFO_View a, UFO_View b)
{
	while (a) {
		UFO_View temp = b;
		while (temp) {
			if (a == temp)
				return temp;
			temp = ufoViewGetParent(temp);
		}
		a = ufoViewGetParent(a);
	}
	return 0;
}

int internal_dispatch_mousemove(UFO_View lastHover, UFO_View curHover)
{
	UFO_View commView = internal_get_commonView(lastHover, curHover);

	while (lastHover && lastHover != commView) {
		ufoViewSendMessage(lastHover, MSG_TOUCH_LEAVE, 0, 0);
		lastHover = ufoViewGetParent(lastHover);
	}

	while (curHover && curHover != commView) {
		ufoViewSendMessage(curHover, MSG_TOUCH_ENTER, 0, 0);
		curHover = ufoViewGetParent(curHover);
	}

	return 1;
}

float internal_distance(ufoPoint *a, ufoPoint *b)
{
    float dx = a->x - b->x;
    float dy = a->y - b->y;
    return sqrt(dx*dx+dy*dy);
}

int rootView_Proc(UFO_View view, int id, void* wParam, void* lParam)
{
	switch (id)
	{
	case MSG_PAINT:
		{
			UFO_PAINT_STRUCT paint = {0, 1.0f, 0};

			ufoRect rectFresh;
			ufoRect rcScreen; 

			ufoClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            ufoClearDepth(0.0);
			ufoClearStencil(0);

			ufoGetScreenRect(&rcScreen);
            
			ufoSetViewPort(&rcScreen);
			ufoPresent2D(rcScreen.right-rcScreen.left, rcScreen.bottom-rcScreen.top);
			ufoRenderIdentityMatrix(UFO_MODELVIEW);
			ufoGetFreshRect(&rectFresh);
			//if (!g_screenRenderTarget || !ufoGetStencilTest())
			rectFresh = rcScreen;

			ufoSetStencilFunc(RENDER_CMP_ALWAYS);
			ufoSetStencilTestRef(0);
			ufoSetStencilOp(UFO_STENCIL_KEEP, UFO_STENCIL_KEEP, UFO_STENCIL_KEEP);
			ufoSetAlphaBlendFactor(ALPHABLEND_ONE, ALPHABLEND_ONE_MINUS_SRC_ALPHA);

			recursive_Paint(view, id, &rectFresh, &paint);
		}
		break;

	case MSG_SIZE_CHANGE:
		{
			return recursive_SizeChange(view, id, wParam, lParam);
		}
		break;

	case MSG_CREATE:
		{
			break;
		}

	case MSG_GET_CONTAINER:
		{
			return 0;
		}
		break;

	case MSG_NC_HITTEST:
		{
			ufoPoint *pt = wParam;
			UFO_View view = ufoViewFromPoint(pt->x, pt->y);
			if (view != g_rootView) {
				if (ufoViewGetMask(view, UFO_VIEW_BIT_DRAG)) {
					return 1;
				}
				//view = ufoViewGetParent(view);
			}
			return 0;
		}
		break;

	case MSG_TOUCH_DOWN:
	case MSG_TOUCH_UP:
	case MSG_TOUCH_MOVE:
	case MSG_TOUCH_WHEEL:
		{
			UFO_TOUCH_STRUCT touch = {0};
			if (g_captureView) {

				if (g_captureView != g_rootView) {
					UFO_View parentView = g_captureView;
					ufoPoint *pt = (ufoPoint*)wParam;
                    ufoPoint ptView = *pt;
                    int count = *(int*)lParam;
                    
					ufoViewPointToView(g_captureView, &ptView.x, &ptView.y);//### screen to client
					ufoViewSendMessage(g_captureView, id, &ptView, &touch);

                    if (id == MSG_TOUCH_DOWN) {
                        if (count == 2) {//### for pinch gesture
                            UFO_View view_2 = ufoViewFromPoint(pt[1].x, pt[1].y);
                            if (g_captureView == view_2) {
                                g_internal_is_pinch = 1;
                                g_internal_touchDown_point[1] = *(pt+1);
                            }
                        }
                    }
                    
					if (id == MSG_TOUCH_MOVE) {//### pan gesture

						if (!g_internal_is_pan && !g_internal_is_pinch) {
							ufoPoint curPt = *(ufoPoint*)wParam;

							if (fabs(curPt.x - g_internal_touchDown_point[0].x) >= 9 ||
								fabs(curPt.y - g_internal_touchDown_point[0].y) >= 9
								) {
								g_internal_is_pan = 1;
								g_internal_touchDown_point[0] = curPt;
							}
						}

						if (g_internal_is_pan) {

							while (parentView != g_rootView) {
								if ((ufoViewGetGesture(parentView) & GESTURE_PAN) ||
                                    (ufoViewGetGesture(parentView) & GESTURE_SWIPE)
                                    )
									break;//### parent view has gesture

								parentView = ufoViewGetParent(parentView);
							}

							if (parentView != g_rootView) {
								UFO_TOUCH_STRUCT touch = {0};
								ufoPoint curPt2 = *(ufoPoint*)wParam;
								//ufoGetTouchPos(&curPt2.x, &curPt2.y);

								curPt2.x -= g_internal_touchDown_point[0].x;
								curPt2.y -= g_internal_touchDown_point[0].y;
                                
								ufoViewSendMessage(parentView, MSG_GESTURE_PAN, &curPt2, &touch);
							}
						}
                        
                        if (g_internal_is_pinch) {
                            int count = *(int*)lParam;
                            if (count <= 1) {
                                ufoLog("pinch", "only one");
                            }
                            
                            while (parentView != g_rootView) {
								if (ufoViewGetGesture(parentView) & GESTURE_PINCH)
									break;//### parent view has gesture
                                
								parentView = ufoViewGetParent(parentView);
							}
                            
							if (parentView != g_rootView) {
                                UFO_TOUCH_STRUCT touch = {0};
                                ufoPoint *pt_now = (ufoPoint*)wParam;
                                float dis_lst = internal_distance(&g_internal_touchDown_point[0], &g_internal_touchDown_point[1]);
                                float dis_now = internal_distance(pt_now, pt_now+1);
                                
                                UFO_GESTURE_PINCH_STRUCT pinch = {0};
                                pinch.point[0] = *pt_now;
                                pinch.point[1] = *(pt_now+1);
                                pinch.scale = dis_now / dis_lst;
                                
                                ufoViewSendMessage(parentView, MSG_GESTURE_PINCH, &pinch, &touch);
                                //ufoLog("pinch", "lst = %f now = %f", dis_lst, dis_now);
                                ufoLog("pinch", "scale = %f", pinch.scale);
                            }
                        }
					}

					if (id == MSG_TOUCH_UP) {
						g_captureView = 0;
                        g_internal_is_pinch = 0;
						//internal_route_touchMove(wParam, lParam);
						g_internal_is_pan = 0;
					}
				}
			}
			else {
				ufoPoint *pt = wParam;
				UFO_View view = ufoViewFromPoint(pt->x, pt->y);
                int count = *(int*)lParam;
                
				if (view && view != g_rootView) {
					ufoPoint ptView = *pt;
					ufoViewPointToView(view, &ptView.x, &ptView.y);//### screen to client
					ufoViewSendMessage(view, id, &ptView, &touch);//### route to top view
                    
                    if (count == 2) {//### for pinch gesture
                        UFO_View view_2 = ufoViewFromPoint(pt[1].x, pt[1].y);
                        if (view == view_2) {
                            g_internal_is_pinch = 1;
                            g_internal_touchDown_point[1] = *(pt+1);
                            //ufoViewPointToView(view, &pt[1].x, &pt[1].y);
                        }
                    }
				}

                //### not support mouse move forever
				/*if (id == MSG_TOUCH_MOVE) {
					internal_dispatch_mousemove(g_hoverView, view);
					g_hoverView = view;
				}*/

				if (id == MSG_TOUCH_DOWN) {
					g_captureView = view;
					g_hoverView = view;
                    g_internal_touchDown_point[0] = *pt;
					//ufoGetTouchPos(&g_internal_touchDown_point[0].x, &g_internal_touchDown_point[0].y);
				}
			}
		}
		break;
	}
	return 0;
}

int ufoViewDefaultProc(UFO_View view, int id, void* wParam, void* lParam)
{
	switch (id)
	{
	case MSG_PAINT:
		{
			PUFO_View_Internal pView = (PUFO_View_Internal)ufoObjectFromHandle((UFO_Handle)view);
			if (pView) {
				ufoRect dst = pView->rect;
				UFO_PAINT_STRUCT *paint = (UFO_PAINT_STRUCT*)lParam;

				ufoTextureDraw(0, pView->background, paint->mask, &dst, (float)paint->stencilTest, paint->color, paint->alpha, 0.0f);
			}
			break;
		}

	case MSG_TOUCH_DOWN:
		{
			PUFO_View_Internal pView = (PUFO_View_Internal)ufoObjectFromHandle((UFO_Handle)view);
			ufoPoint *point = (ufoPoint*)wParam;
			UFO_TOUCH_STRUCT *touch = (UFO_TOUCH_STRUCT*)lParam;

			if (pView) {
				if (pView->onTouchDown) {
					ufoScriptRun(view, pView->onTouchDown, wParam, lParam);
				}

				if (!touch->bHandled && pView->parentView->handle != g_rootView) {//### route message to parent

					point->x += pView->rect.left;
					point->y += pView->rect.top;
					return ufoViewSendMessage(pView->parentView->handle, id, wParam, lParam);
				}
			}
		}
		break;

	case MSG_TOUCH_UP:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			ufoPoint *point = wParam;
			UFO_TOUCH_STRUCT *touch = lParam;

			if (pView) {
				if (pView->onTouchUp) {
					ufoScriptRun(view, pView->onTouchUp, wParam, lParam);
				}

				if (point->x >= 0 && point->y >= 0 && 
					point->x < pView->rect.right-pView->rect.left && point->y < pView->rect.bottom-pView->rect.top) {
						ufoViewSendMessage(view, MSG_TOUCH_CLICK, wParam, 0);
				}

				if (pView->touchTrack) {
					int nSize = ufoStackGetSize(pView->touchTrack);
					if (nSize) {
						int i = 0;
						int trackCount = 0;
						long timeStamp = ufoGetCurTime();
                        long nearTime = timeStamp;
						ufoPoint *track = (ufoPoint*)malloc(sizeof(ufoPoint)*nSize);
						for (i = 0; i < nSize; ++i) {
							UFO_Touch_Track *touchTrack = (UFO_Touch_Track*)ufoStackGetElement(pView->touchTrack, i);
							if (touchTrack && timeStamp - touchTrack->timeStamp <= GESTURE_SWIPE_TIME) {
								track[trackCount] = touchTrack->point;
								trackCount++;
                                
                                if (touchTrack->timeStamp < nearTime) {
                                    nearTime = touchTrack->timeStamp;
                                }
							}
						}

						if (trackCount >= 2) {
							float dx = track[0].x-track[trackCount-1].x;
							float dy = track[0].y-track[trackCount-1].y;
							if (fabs(dx) > 15.0f || fabs(dy) > 15.0f) {
                                UFO_GESTURE_SWIPE_STRUCT swipe = {0};
                                swipe.point = track;
                                swipe.numberOfPoint = trackCount;
                                swipe.deltaTime = timeStamp-nearTime;
								ufoViewSendMessage(view, MSG_GESTURE_SWIPE, &swipe, 0);
								ufoStackReSize(pView->touchTrack, 0);   
							}
						}
						free(track);
					}
				}

				if (!touch->bHandled && pView->parentView->handle != g_rootView) {//### route message to parent

					point->x += pView->rect.left;
					point->y += pView->rect.top;
					return ufoViewSendMessage(pView->parentView->handle, id, wParam, lParam);
				}
			}
		}
		break;

	case MSG_TOUCH_CLICK:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			if (pView && pView->onTouchClick) {		
				ufoScriptRun(view, pView->onTouchClick, wParam, lParam);
			}
		}
		break;

	case MSG_SIZE_CHANGE:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			if (pView && pView->onRectChange) {		
				ufoScriptRun(view, pView->onRectChange, wParam, lParam);
			}
		}
		break;

	case MSG_TOUCH_MOVE:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			ufoPoint *point = wParam;
			UFO_TOUCH_STRUCT *touch = lParam;

			if (!touch->bHandled && pView->parentView->handle != g_rootView) {//### route message to parent

				point->x += pView->rect.left;
				point->y += pView->rect.top;
				return ufoViewSendMessage(pView->parentView->handle, id, wParam, lParam);
			}
		}
		break;

	case MSG_GESTURE_SWIPE:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			if (pView && pView->onGestureSwipe) {		
				ufoScriptRun(view, pView->onGestureSwipe, wParam, lParam);
			}
		}
		break;

	case MSG_GESTURE_PAN:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);

			if (pView) {

				UFO_Touch_Track touchTrack;
				touchTrack.point = *(ufoPoint*)wParam;
				ufoViewPointToScreen(view, &touchTrack.point.x, &touchTrack.point.y);
				touchTrack.timeStamp = ufoGetCurTime();

				if (!pView->touchTrack)
					pView->touchTrack = ufoStackCreate(sizeof(UFO_Touch_Track));

				if (ufoStackGetSize(pView->touchTrack)) {
					UFO_Touch_Track *lstElement = ufoStackGetElement(pView->touchTrack, ufoStackGetSize(pView->touchTrack)-1);
					if (lstElement && ufoGetCurTime()-lstElement->timeStamp > GESTURE_SWIPE_TIME) {
						ufoStackReSize(pView->touchTrack, 0);
					}
				}

				ufoStackPush(pView->touchTrack, &touchTrack);
			}

			/*if (!touch->bHandled && pView->parentView->handle != g_rootView) {//### route message to parent

			point->x += pView->rect.left;
			point->y += pView->rect.top;
			return ufoViewSendMessage(pView->parentView->handle, id, wParam, lParam);
			}*/
		}
		break;

	case MSG_ANIMATION_FINISH:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			if (pView && pView->onAnimationFinish) {		
				ufoScriptRun(view, pView->onAnimationFinish, wParam, lParam);
			}		
		}
		break;

	case MSG_GET_CONTAINER:
		{
			return (int)view;
		}

	case MSG_CREATE:
		{
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			if (pView && pView->onInit) {
				ufoScriptRun(view, pView->onInit, wParam, lParam);
			}
			break;
		}

	case MSG_XML:
		{
			xmlNodePtr node = wParam;
			//UFO_Resource res = ufoResourceGetDefault();

			xmlChar *id = xmlGetProp(node, (const xmlChar*)"id");
			xmlChar *rect = xmlGetProp(node, (const xmlChar*)"rect");
			xmlChar *margin = xmlGetProp(node, (const xmlChar*)"margin");
			xmlChar *textanchor = xmlGetProp(node, (const xmlChar*)"anchor");
			xmlChar *background = xmlGetProp(node, (const xmlChar*)"background");
			xmlChar *alpha = xmlGetProp(node, (const xmlChar*)"alpha");
			xmlChar *angle = xmlGetProp(node, (const xmlChar*)"angle");
			xmlChar *hotSpot = xmlGetProp(node, (const xmlChar*)"hotSpot");
			xmlChar *axis = xmlGetProp(node, (const xmlChar*)"axis");
			xmlChar *drag = xmlGetProp(node, (const xmlChar*)"drag");
			xmlChar *fovy = xmlGetProp(node, (const xmlChar*)"fovy");
			xmlChar *autoSize = xmlGetProp(node, (const xmlChar*)"autoSize");
			xmlChar *zOrder = xmlGetProp(node, (const xmlChar*)"zOrder");
			xmlChar *clipsTobounds = xmlGetProp(node, (const xmlChar*)"clipsTobounds");
			xmlChar *cornerRadius = xmlGetProp(node, (const xmlChar*)"cornerRadius");
			xmlChar *hidden = xmlGetProp(node, (const xmlChar*)"hidden");
			xmlChar *perspective = xmlGetProp(node, (const xmlChar*)"perspective");
			xmlChar *backgroundColor = xmlGetProp(node, (const xmlChar*)"backgroundColor");
            xmlChar *gesture = xmlGetProp(node, (const xmlChar*)"gesture");
            
			if (id) {
				ufoViewSetId(view, (char*)id);
				xmlFree(id);
			}

			if (hidden) {
				if (strcmp((const char*)hidden, "true") == 0)
					ufoViewSetMask(view, UFO_VIEW_BIT_HIDDEN, 1);
				xmlFree(hidden);
			}
            
            if (gesture) {
                UFO_GESTURE _gesture = 0;
				if (strstr((const char*)textanchor, "PAN"))
					_gesture |= GESTURE_PAN;
				if (strstr((const char*)textanchor, "PINCH"))
					_gesture |= GESTURE_PINCH;
                if (strstr((const char*)textanchor, "SWIPE"))
					_gesture |= GESTURE_SWIPE;
                ufoViewSetGesture(view, _gesture);
				xmlFree(gesture);
			}

			if (rect) {
				ufoRect rectSrc = {0, 0, 0, 0};
				sscanf((const char*)rect, "%f, %f, %f, %f", &rectSrc.left, &rectSrc.top, &rectSrc.right, &rectSrc.bottom);
				ufoViewSetRect(view, &rectSrc);
				xmlFree(rect);
			}

			if (autoSize) {
				if (strcmp((const char*)autoSize, "true") == 0)
					ufoViewSetMask(view, UFO_VIEW_BIT_AUTOSIZE, 1);
				xmlFree(autoSize);
			}

			if (clipsTobounds) {
				if (strcmp((const char*)clipsTobounds, "true") == 0)
					ufoViewSetMask(view, UFO_VIEW_BIT_CLIPSTOBOUNDS, 1);
				xmlFree(clipsTobounds);
			}

			if (cornerRadius) {
				float _cornerRadius = 0;
				sscanf((const char*)cornerRadius, "%f", &_cornerRadius);
				ufoViewSetCornerRadius(view, _cornerRadius);
				xmlFree(cornerRadius);
			}

			if (zOrder) {
				float _zOrder = 0;
				sscanf((const char*)zOrder, "%f", &_zOrder);
				ufoViewSetZorder(view, _zOrder);
				xmlFree(zOrder);
			}

			if (textanchor) {
				UFO_LAYOUT_ANCHOR anchor = 0;
				if (strstr((const char*)textanchor, "ANCHOR_LEFT"))
					anchor |= ANCHOR_LEFT;

				if (strstr((const char*)textanchor, "ANCHOR_TOP"))
					anchor |= ANCHOR_TOP;

				if (strstr((const char*)textanchor, "ANCHOR_RIGHT"))
					anchor |= ANCHOR_RIGHT;

				if (strstr((const char*)textanchor, "ANCHOR_BOTTOM"))
					anchor |= ANCHOR_BOTTOM;

				if (strstr((const char*)textanchor, "ANCHOR_CENTER_HORZ"))
					anchor |= ANCHOR_CENTER_HORZ;

				if (strstr((const char*)textanchor, "ANCHOR_CENTER_VERT"))
					anchor |= ANCHOR_CENTER_VERT;

				ufoViewSetAnchor((UFO_View)view, anchor);
				xmlFree(textanchor);
			}

			if (margin) {
				ufoRect rectMargin = {0, 0, 0, 0};
				sscanf((const char*)margin, "%f, %f, %f, %f",
					&rectMargin.left, &rectMargin.top, &rectMargin.right, &rectMargin.bottom);
				ufoViewSetMargin(view, &rectMargin);
				xmlFree(margin);
			}

			if (drag) {
				if (strcmp((const char*)drag, "true") == 0)
					ufoViewSetMask(view, UFO_VIEW_BIT_DRAG, 1);
				xmlFree(drag);
			}

			if (fovy) {
				float ffovy = 1.0f;
				sscanf((const char*)fovy, "%f", &ffovy);
				ufoViewSetFovy(view, ffovy);
				xmlFree(fovy);
			}

			if (background) {
				UFO_Texture tex = ufoResourceGetTexture((const char*)background);
				ufoViewSetBackground(view, tex);
				xmlFree(background);
			}

			if (hotSpot) {
				float x = 0, y = 0;
				sscanf((const char*)hotSpot, "%f, %f", &x, &y);
				ufoViewSetHotSpot(view, x, y);
				xmlFree(hotSpot);
			}

			if (axis) {
				char str[256] = {0};
				UFO_AXIS _axis = 0;
				sscanf((const char*)axis, "%s", str);
				str[255] = 0;
				if (strstr(str, "x") || strstr(str, "X"))
					_axis |= AXIS_X;
				if (strstr(str, "y") || strstr(str, "Y"))
					_axis |= AXIS_Y;
				if (strstr(str, "z") || strstr(str, "Z"))
					_axis |= AXIS_Z;
				ufoViewSetAxis(view, _axis);
				xmlFree(axis);
			}

			if (angle) {
				float fAngle = 0.0f;
				sscanf((const char*)angle, "%f", &fAngle);
				ufoViewSetAngle(view, fAngle);
				xmlFree(angle);
			}

			if (alpha) {
				float fAlpha = 1.0f;
				sscanf((const char*)alpha, "%f", &fAlpha);
				ufoViewSetAlpha(view, fAlpha);
				xmlFree(alpha);
			}

			if (perspective) {
				if (strcmp((const char*)perspective, "true") == 0)
					ufoViewSetPerspective(view, 1);
				xmlFree(perspective);
			}

			if (backgroundColor) {
				ufoColor color = {0};
				sscanf((const char*)backgroundColor, "%f, %f, %f, %f", &color.red, &color.green, &color.blue, &color.alpha);
				ufoViewSetBackgroundColor(view, &color);
				xmlFree(backgroundColor);
			}
		}
		break;

	case MSG_TOUCH_ENTER:
		{
			//PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			//ufoLog("TouchEnter", "id = %s enter\n", pView->id);
			break;
		}

	case MSG_TOUCH_LEAVE:
		{
			//PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
			//ufoLog("TouchLeave", "id = %s leave\n", pView->id);
			break;
		}
	}
	return 0;
}

int ufoViewSetPerspective(UFO_View view, int perspective)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		pView->perspective = perspective;
		return 1;
	}
	return 0;
}

int ufoViewSetNeedPaint(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		ufoRect rectView = pView->rect;
		ufoRectOffset(&rectView, -rectView.left, -rectView.top);
		ufoViewPointToScreen(view, &rectView.left, &rectView.top);
		ufoViewPointToScreen(view, &rectView.right, &rectView.bottom);

		ufoAddFreshRect(&rectView);
	}
	return 0;
}

int internal_rootView_init()
{
	if (!g_rootView) {

		PUFO_View_Internal pRootView = malloc(sizeof(UFO_View_Internal));
		memset(pRootView, 0, sizeof(UFO_View_Internal));

		pRootView->anchor = ANCHOR_LEFT|ANCHOR_TOP|ANCHOR_RIGHT|ANCHOR_BOTTOM;
		pRootView->proc = rootView_Proc;//### setup default message callback

		g_rootView = (UFO_View)ufoCreateHandle(UFO_ID_VIEW, pRootView);
		pRootView->handle = g_rootView;
		g_hoverView = g_rootView;

		ufoViewSetNeedPaint(g_rootView);

		ufoTemplateAddReg("View", ufoViewDefaultProc);
		ufoTemplateAddReg("Thumb", default_thumb_proc);
		ufoTemplateAddReg("ScrollBar", default_scrollBar_proc);
		ufoTemplateAddReg("ScrollView", default_scrollView_proc);
		ufoTemplateAddReg("ContentView", default_content_proc);
		ufoTemplateAddReg("Static", default_static_proc);
		ufoTemplateAddReg("Button", default_button_proc);
		ufoTemplateAddReg("FlipView", default_flipView_proc);
		ufoTemplateAddReg("WaterView", default_waterView_proc);
		ufoTemplateAddReg("PageControl", default_pagecontrol_proc);
		ufoTemplateAddReg("TabView", default_tabview_proc);
		ufoTemplateAddReg("TabBar", default_tabbar_proc);

		internal_initializationScript_animation();
		internal_initializationScript_view();
		internal_initializationScript_flipview();
		internal_initializationScript_static();
		internal_initializationScript_buttonview();
		internal_initializationScript_scrollview();
		internal_initializationScript_pagecontrol();
		internal_initializationScript_tabview();
		internal_initializationScript_tabbar();
	}
	return 1;
}

UFO_View ufoViewGetDefault()
{
	return g_rootView;
}

int recursive_CreateViewFromXML(UFO_View parentView, xmlNodePtr node)
{
	while (node) {

		if (node->type != XML_ELEMENT_NODE) {
			//### not element node
		}
		else if (xmlStrcmp(node->name, (const xmlChar*)"OnTouchDown") == 0) {
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)parentView);
			if (pView) {
				pView->onTouchDown = xmlNodeGetContent(node);;
			}
		}
		else if (xmlStrcmp(node->name, (const xmlChar*)"OnTouchUp") == 0) {
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)parentView);
			if (pView) {
				pView->onTouchUp = xmlNodeGetContent(node);
			}
		}
		else if (xmlStrcmp(node->name, (const xmlChar*)"OnTouchClick") == 0) {
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)parentView);
			if (pView) {
				pView->onTouchClick = xmlNodeGetContent(node);
			}
		}
		else if (xmlStrcmp(node->name, (const xmlChar*)"OnInit") == 0) {
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)parentView);
			if (pView) {
				pView->onInit = xmlNodeGetContent(node);;
			}
		}
		else if (xmlStrcmp(node->name, (const xmlChar*)"onRectChange") == 0) {
			PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)parentView);
			if (pView) {
				pView->onRectChange = xmlNodeGetContent(node);;
			}
		}
		else {
			if (node->name) {
				UFO_View view = internal_ufoViewCreate(node->name);
				if (view) {
					internal_ufoViewAddChildView(parentView, view);

					recursive_CreateViewFromXML(view, node->children);

					ufoViewSendMessage(view, MSG_CREATE, 0, 0);

					{//### notify add childview
						UFO_View subView = ufoViewGetChild(view);
						while (subView) {
							ufoViewSendMessage(view, MSG_ADD_CHILDVIEW, subView, 0);
							subView = ufoViewGetNextView(subView);
						}
					}
					ufoViewSendMessage(view, MSG_XML, node, 0);
				}
				else {
					//assert(!"can't create unregister view");
				}
			}
		}
		node = node->next;
	}
	return 0;
}

UFO_View ufoViewCreateFromXML(UFO_View parentView, const char *filename)
{
	xmlDocPtr doc = ufoXmlDocFromFile(filename);
	xmlNodePtr curNode = 0;

	if (!doc)
		return 0;

	curNode = xmlDocGetRootElement(doc);

	if (!curNode || xmlStrcmp(curNode->name, (const xmlChar*)"UFO") != 0) {
		return 0;
	}

	curNode = curNode->children;

	while (curNode) {//### seek to Resource
		if (xmlStrcmp(curNode->name, (const xmlChar*)"UI") == 0)
			break;
		curNode = curNode->next;
	}

	if (!parentView) {
		parentView = ufoViewGetDefault();
	}

	if (curNode && curNode->children) {
		recursive_CreateViewFromXML(parentView, curNode->children);
	}

	return parentView;
}

int ufoViewSendMessage(UFO_View view, int id, void* wParam, void* lParam)
{
	PUFO_View_Internal pView = 0;
	if (view == 0)
		view = g_rootView;

	if (!view) {
		return 0;
	}

	pView = ufoObjectFromHandle((UFO_Handle)view);

	if (pView && pView->proc) {
		return pView->proc(view, id, wParam, lParam);
	}

	return 0;
}

UFO_View internal_ufoViewCreate(const char* className)
{
	PUFO_View_Internal pChildView = 0;
	VIEWPROC proc = ufoTemplateGetReg(className);

	if (!proc && className != 0) {//### find not this bindName
		return 0;
	}

	pChildView = malloc(sizeof(UFO_View_Internal));//### alloc object
	memset(pChildView, 0, sizeof(UFO_View_Internal));

	pChildView->proc = ufoViewDefaultProc;
	if (className) {
		if (proc)
			pChildView->proc = proc;
	}

	pChildView->alpha = 1.0f;
	pChildView->handle = (UFO_View)ufoCreateHandle(UFO_ID_VIEW, pChildView);

	return (UFO_View)pChildView->handle;
}

UFO_View ufoViewCreate(const char* className) 
{
	UFO_View view = internal_ufoViewCreate(className);
	if (view) {
		ufoViewSendMessage(view, MSG_CREATE, 0, 0);
	}
	return view;
}

int internal_ufoViewAddChildView(UFO_View parentView, UFO_View childView)
{
	PUFO_View_Internal pParentView = 0;
	PUFO_View_Internal pChildView = ufoObjectFromHandle((UFO_Handle)childView);

	if (!pChildView || pChildView->parentView) {
		assert("ufoViewAddChildView error");
		return 0;
	}

	parentView = (UFO_View)ufoViewSendMessage(parentView, MSG_GET_CONTAINER, childView, 0);

	if (!parentView)
		parentView = g_rootView;

	pParentView = ufoObjectFromHandle((UFO_Handle)parentView);

	pChildView->parentView = pParentView;

	//### parent changed, so size need reset
	if (pChildView->anchor || (pChildView->mask & UFO_VIEW_BIT_AUTOSIZE) ) {
		ufoRect newRect = pChildView->rect;
		pChildView->rect.left = 1e8;

		recursive_SizeChange(pChildView->handle, MSG_SIZE_CHANGE, &newRect, 0);
	}

	if (!pParentView->childView) {
		pParentView->childView = pChildView;
	}
	else {
		PUFO_View_Internal pNextView = pParentView->childView;
		while (pNextView->nextView) {
			pNextView = pNextView->nextView;
		}

		pNextView->nextView = pChildView;
		pChildView->lastView = pNextView;
	}

	internal_sort_children_views(parentView);

	return 1;
}

int ufoViewAddChildView(UFO_View parentView, UFO_View childView)
{
	int nRet = internal_ufoViewAddChildView(parentView, childView);
	ufoViewSendMessage(parentView, MSG_ADD_CHILDVIEW, childView, 0);
	return nRet;
}

void* ufoViewSetUserData(UFO_View view, void *userdata)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView)
		pView->userData = userdata;
	return 0;
}

void* ufoViewGetUserData(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView)
		return pView->userData;
	return 0;
}

VIEWPROC ufoViewGetProc(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView)
		return pView->proc;
	return 0;
}

VIEWPROC ufoViewSetProc(UFO_View view, VIEWPROC proc)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && proc) {
		VIEWPROC oldProc = pView->proc;
		pView->proc = proc;
		return oldProc;
	}
	return 0;
}

int ufoViewDelete(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return 1;
	}
	return 0;
}

int ufoViewRender(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		recursive_Paint(view, MSG_PAINT, 0, 0);
		return 1;
	}
	return 0;
}

UFO_View ufoViewGetFromId(UFO_View view, const char*id)
{
	UFO_View ret = 0;
	PUFO_View_Internal pView = 0;
	if (!view)
		view = g_rootView;

	pView = ufoObjectFromHandle((UFO_Handle)view);

	while (pView) {

		if (pView->id && strcmp(pView->id, id) == 0) {
			return pView->handle;
		}

		if (pView->childView) {
			ret = ufoViewGetFromId(pView->childView->handle, id);
			if (ret) {
				break;
			}
		}

		pView = pView->nextView;
	}
	return ret;
}

int ufoViewSetCapture(UFO_View view)
{
	g_captureView = view;
	return 0;
}

int ufoViewRelease()
{
	g_captureView = 0;
	return 1;
}

int ufoViewSetMask(UFO_View view, int mask, int value)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		if (value)
			pView->mask |= mask;
		else
			pView->mask &= ~mask;

		if (mask & UFO_VIEW_BIT_HIDDEN) {
			ufoViewSetNeedPaint(view);
		}

		return 1;
	}
	return 0;
}

int ufoViewGetMask(UFO_View view, int mask)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->mask & mask;
	}
	return 0;
}

int ufoViewGetChildCount(UFO_View view)
{
	int nCount = 0;
	PUFO_View_Internal pView = 0;
	if (!view)
		view = g_rootView;
	pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && pView->childView) {
		pView = pView->childView;
		while (pView) {
			nCount++;
			pView = pView->nextView;
		}
		return nCount;
	}
	return 0;
}

UFO_View ufoViewGetChild(UFO_View view)
{
	PUFO_View_Internal pView = 0;
	if (!view)
		view = g_rootView;

	pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && pView->childView) {
		return pView->childView->handle;
	}

	return 0;
}

UFO_View ufoViewGetChildByIndex(UFO_View view, int nIndex)
{
	PUFO_View_Internal pView = 0;
	if (!view)
		view = g_rootView;

	pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && pView->childView) {
		pView = pView->childView;
		while (pView && nIndex) {
			nIndex--;
			pView = pView->nextView;
		}
		if (nIndex == 0) {
			return pView->handle;
		}
	}
	return 0;
}

UFO_View ufoViewGetParent(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && pView->parentView) {
		return pView->parentView->handle;
	}

	return 0;
}

UFO_View ufoViewGetNextView(UFO_View view)
{
	PUFO_View_Internal pView = 0;
	if (!view)
		view = g_rootView;

	pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && pView->nextView) {
		return pView->nextView->handle;
	}

	return 0;
}

UFO_View ufoViewGetLastView(UFO_View view)
{
	PUFO_View_Internal pView = 0;
	if (!view)
		view = g_rootView;

	pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && pView->lastView) {
		return pView->lastView->handle;
	}

	return 0;
}

int ufoViewSetId(UFO_View view, const char *id)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && id) {
		int length = strlen(id);

		if (pView->id) {
			free(pView->id);
		}

		pView->id = malloc(length+1);
		strcpy(pView->id, id);
	}

	return 0;
}

const char* ufoViewGetId(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);

	if (pView)
		return pView->id;

	return 0;
}

int ufoViewSetRect(UFO_View view, const ufoRect *rect)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && rect) {
		if (!ufoRectEqual(rect, &pView->rect)) {
			ufoRect newRect = *rect;
			ufoViewSetNeedPaint(view);//### old area need redraw
			return recursive_SizeChange(view, MSG_SIZE_CHANGE, &newRect, 0);
		}
	}
	return 0;
}

int ufoViewGetRect(UFO_View view, ufoRect * rect)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && rect) {
		*rect = pView->rect;
		return 1;
	}
	return 0;
}

int ufoViewGetMargin(UFO_View view, ufoRect * rect)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && rect) {
		*rect = pView->margin;
		return 1;
	}
	return 0;
}

int ufoViewSetBackgroundColor(UFO_View view, ufoColor *color)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		if (color == 0) {		
			if (pView->backgroundColor) {
				free(pView->backgroundColor);
				pView->backgroundColor = 0;
			}
		}
		else {
			if (!pView->backgroundColor) {
				pView->backgroundColor = (ufoColor*)malloc(sizeof(ufoColor));	
			}
			memcpy(pView->backgroundColor, color, sizeof(ufoColor));
		}
		return 1;
	}
	return 0;
}

ufoColor* ufoViewGetBackgroundColor(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->backgroundColor;
	}
	return 0;
}

float ufoViewGetAlpha(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->alpha;
	}
	return 0.0f;
}

float ufoViewGetZorder(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->zOrder;
	}
	return 0.0f;
} 

float ufoViewGetAngle(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->angle;
	}
	return 0.0f;
}

float ufoViewGetZ(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->z;
	}
	return 0.0f;
}

int ufoViewSetAngle(UFO_View view, float angle)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {		
		if (angle >= 360.0f) {
			//angle = ((int)(angle))%360;
		}

		pView->angle = angle;
		ufoViewSetNeedPaint(view);
	}

	return 1;
}

int ufoViewSetHotSpot(UFO_View view, float x, float y)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		pView->hotSpot_x = x;
		pView->hotSpot_y = y;
		ufoViewSetNeedPaint(view);
		return 1;
	}
	return 0;
}

int ufoViewSetAxis(UFO_View view, UFO_AXIS axis)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		pView->axis = axis;
		ufoViewSetNeedPaint(view);
		return 1;
	}
	return 0;
}

int ufoViewSetZ(UFO_View view, float z)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		pView->z = z;
		ufoViewSetNeedPaint(view);
		return 1;
	}
	return 0;
}

int ufoViewSetAlpha(UFO_View view, float alpha)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		pView->alpha = alpha;
		ufoViewSetNeedPaint(view);
		return 1;
	}
	return 0;
}

int ufoViewSetCornerRadius(UFO_View view, float cornerRadius)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		pView->cornerRadius = cornerRadius;
		ufoViewSetNeedPaint(view);
		return 1;
	}
	return 0;
}

int ufoViewSetZorder(UFO_View view, float zOrder)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		UFO_View parentView = ufoViewGetParent(view);
		if (parentView) {
            pView->zOrder = zOrder;
			internal_sort_children_views(parentView);
			ufoViewSetNeedPaint(parentView);
		}
		return 1;
	}
	return 0;
}

int ufoViewSetFovy(UFO_View view, float fovy)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		pView->fovy = fovy;
		ufoViewSetNeedPaint(view);
		return 1;
	}
	return 0;
}

int ufoViewSetAnchor(UFO_View view, UFO_LAYOUT_ANCHOR anchor)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		if (pView->anchor != anchor) {
			ufoRect newRect = pView->rect;
			pView->rect.left = 1e8;
			pView->anchor = anchor;
			ufoViewSetRect(view, &newRect);
		}
		return 1;
	}

	return 0;
}

int ufoViewSetMargin(UFO_View view, const ufoRect *rect)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && rect) {
		ufoRect newRect = pView->rect;
		pView->rect.left = 1e8;
		pView->margin = *rect;
		ufoViewSetRect(view, &newRect);
		return 1;
	}

	return 0;
}

int ufoViewPointToScreen(UFO_View view, float *x, float *y)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		while (pView) {
			*x += pView->rect.left;
			*y += pView->rect.top;
			pView = pView->parentView;
		}
		return 1;
	}
	return 0;
}

int ufoViewRectToScreen(UFO_View view, ufoRect *rect)
{
	return ufoViewPointToScreen(view, &rect->left, &rect->top) &&
		ufoViewPointToScreen(view, &rect->right, &rect->bottom);
}

int ufoViewPointToView(UFO_View view, float *x, float *y)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		while (pView) {
			*x -= pView->rect.left;
			*y -= pView->rect.top;
			pView = pView->parentView;
		}
		return 1;
	}
	return 0;
}

int ufoViewIsVisible(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		while (pView) {
			if (ufoViewGetMask(pView->handle, UFO_VIEW_BIT_HIDDEN)) {
				return 0;
			}
			pView = pView->parentView;
		}
		return 1;
	}
	return 0;
}

int ufoViewSetOnTouchDown(UFO_View view, const char *script)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && script) {
		if (pView->onTouchDown) {
			xmlFree(pView->onTouchDown);
		}
		pView->onTouchDown = malloc(strlen(script)+1);
		strcpy(pView->onTouchDown, script);
		return 1;
	}
	return 0;
}

int ufoViewSetOnAnimationFinish(UFO_View view, const char *script)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && script) {
		if (pView->onAnimationFinish) {
			xmlFree(pView->onAnimationFinish);
		}
		pView->onAnimationFinish = malloc(strlen(script)+1);
		strcpy(pView->onAnimationFinish, script);
		return 1;
	}
	return 0;
}

int ufoViewSetOnRectChange(UFO_View view, const char *script)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && script) {
		if (pView->onRectChange) {
			xmlFree(pView->onRectChange);
		}
		pView->onRectChange = malloc(strlen(script)+1);
		strcpy(pView->onRectChange, script);
		return 1;
	}
	return 0;
}

int ufoViewSetOnTouchUp(UFO_View view, const char *script)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && script) {
		if (pView->onTouchUp) {
			xmlFree(pView->onTouchUp);
		}
		pView->onTouchUp = malloc(strlen(script)+1);
		strcpy(pView->onTouchUp, script);
		return 1;
	}
	return 0;
}

int ufoViewSetOnClick(UFO_View view, const char *script)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView && script) {
		if (pView->onTouchClick) {
			xmlFree(pView->onTouchClick);
		}
		pView->onTouchClick = malloc(strlen(script)+1);
		strcpy(pView->onTouchClick, script);
		return 1;
	}
	return 0;
}

PUFO_View_Internal recursive_viewFromPoint(PUFO_View_Internal pView, float x, float y)
{
	if (pView) {

		PUFO_View_Internal pChildView = pView->childView;

		x -= pView->rect.left;
		y -= pView->rect.top;

		while (pChildView && pChildView->nextView) {//### seek to lastChildView
			pChildView = pChildView->nextView;
		}

		while (pChildView) {

			if (ufoRectIsPointIn(&pChildView->rect, x, y) && //### point in view's rect
				!ufoViewGetMask(pChildView->handle, UFO_VIEW_BIT_HIDDEN)) {//### view is visible
					return recursive_viewFromPoint(pChildView, x, y);
			}

			pChildView = pChildView->lastView;
		}
	}

	return pView;
}

int ufoViewSetBackground(UFO_View view, UFO_Texture texture)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		if (pView->background != texture) {
			pView->background = texture;
			ufoViewSetNeedPaint(view);
		}
		return 1;
	}
	return 0;
}

UFO_Texture ufoViewGetBackground(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->background;
	}
	return 0;
}

UFO_View ufoViewFromPoint(float x, float y)
{
	PUFO_View_Internal pView = recursive_viewFromPoint(ufoObjectFromHandle((UFO_Handle)g_rootView), x, y);
	if (pView) {
		return (UFO_View)pView->handle;
	}
	return 0;
}

int ufoViewSetGesture(UFO_View view, UFO_GESTURE gesture)
{
 	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
        pView->gesture = gesture;
		return 1;
	}
	return 0;
}

UFO_GESTURE ufoViewGetGesture(UFO_View view)
{
	PUFO_View_Internal pView = ufoObjectFromHandle((UFO_Handle)view);
	if (pView) {
		return pView->gesture;
	}
	return 0;
}





